#!/usr/bin/python3
# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t -*-

import os
import glob
import signal
import logging
import subprocess
from mc_util import McUtil


class Advertiser:

    # FIXME: need to auto refresh when zim files in storage directory changed

    @staticmethod
    def get_properties():
        return {
            "need-temp-directory": True,
            "storage-dependencies": ["file"],
        }

    def __init__(self, param):
        self._tmpDir = param["temp-directory"]
        self._libraryFile = os.path.join(self._tmpDir, "library.xml")
        self._listenIp = param["listen-ip"]

        self._port = None
        self._proc = None
        self._mirrorSiteDict = dict()
        self._advertisedMirrorSiteIdList = []
        try:
            self._generateLibraryXmlFromScratch()
            self._port = McUtil.getFreeSocketPort("tcp")
            self._proc = self._startProc()
            McUtil.waitSocketPortForProc("tcp", self._listenIp, self._port, self._proc)
            logging.info("Advertiser (kiwix) started, listening on port %d." % (self._port))
        except Exception:
            self.dispose()
            raise

    def dispose(self):
        if self._proc is not None:
            self._proc.terminate()
            self._proc.wait()
            self._proc = None
        if self._port is not None:
            self._port = None
        McUtil.forceDelete(self._libraryFile)

    def add_mirror_site(self, mirror_site_id, mirror_site_properties):
        assert mirror_site_id not in self._mirrorSiteDict
        self._mirrorSiteDict[mirror_site_id] = mirror_site_properties

    def advertise_mirror_site(self, mirror_site_id):
        assert mirror_site_id in self._mirrorSiteDict and mirror_site_id not in self._advertisedMirrorSiteIdList
        self._advertisedMirrorSiteIdList.append(mirror_site_id)

        self._addToLibraryXml(mirror_site_id)
        os.kill(self._proc.pid, signal.SIGHUP)      # SIGHUP makes kiwix-serve process reload libraries

    def remove_mirror_site(self, mirror_site_id):
        assert mirror_site_id in self._mirrorSiteDict
        if mirror_site_id in self._advertisedMirrorSiteIdList:
            self._advertisedMirrorSiteIdList.remove(mirror_site_id)
        del self._mirrorSiteDict[mirror_site_id]

        self._generateLibraryXmlFromScratch()
        os.kill(self._proc.pid, signal.SIGHUP)      # SIGHUP makes kiwix-serve process reload libraries

    def get_access_info(self, mirror_site_id):
        assert mirror_site_id in self._mirrorSiteDict
        return {
            "url": "http://{IP}:%d/%s" % (self._port, mirror_site_id),
            "description": "",
        }

    def _generateLibraryXmlFromScratch(self):
        # create an empty library file
        with open(self._libraryFile, "w") as f:
            f.write('<?xml version="1.0"?>\n')
            f.write('<library>\n')
            f.write('</library>\n')

        # add libraries
        for msId in self._advertisedMirrorSiteIdList:
            self._addToLibraryXml(msId)

    def _addToLibraryXml(self, msId):
        for fullfn in glob.glob(os.path.join(self._mirrorSiteDict[msId]["storage-param"]["file"]["data-directory"], "**", "*.zim")):
            McUtil.cmdCall("/usr/bin/kiwix-manage", self._libraryFile, "add", fullfn)

    def _startProc(self):
        # kiwix-serve prints redundant messages to stdout, so we redirect stdout to /dev/null
        # don't use "-M", we use signal to make kiwix-server reload libraries
        return subprocess.Popen([
            "/usr/bin/kiwix-serve",
            "--library",
            "--address=%s" % (self._listenIp),
            "--port=%d" % (self._port),
            self._libraryFile,
        ], stdout=subprocess.DEVNULL, cwd=self._tmpDir)
